package spnego
import (
"context"
"errors"
"fmt"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/gssapi"
"github.com/jcmturner/gokrb5/v8/messages"
"github.com/jcmturner/gokrb5/v8/service"
"github.com/jcmturner/gokrb5/v8/types"
)
const (
NegStateAcceptCompleted NegState = 0
NegStateAcceptIncomplete NegState = 1
NegStateReject NegState = 2
NegStateRequestMIC NegState = 3
)
type NegState int
type NegTokenInit struct {
MechTypes []asn1 .ObjectIdentifier
ReqFlags asn1 .BitString
MechTokenBytes []byte
MechListMIC []byte
mechToken gssapi .ContextToken
settings *service .Settings
}
type marshalNegTokenInit struct {
MechTypes []asn1 .ObjectIdentifier `asn1:"explicit,tag:0"`
ReqFlags asn1 .BitString `asn1:"explicit,optional,tag:1"`
MechTokenBytes []byte `asn1:"explicit,optional,omitempty,tag:2"`
MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"`
}
type NegTokenResp struct {
NegState asn1 .Enumerated
SupportedMech asn1 .ObjectIdentifier
ResponseToken []byte
MechListMIC []byte
mechToken gssapi .ContextToken
settings *service .Settings
}
type marshalNegTokenResp struct {
NegState asn1 .Enumerated `asn1:"explicit,tag:0"`
SupportedMech asn1 .ObjectIdentifier `asn1:"explicit,optional,tag:1"`
ResponseToken []byte `asn1:"explicit,optional,omitempty,tag:2"`
MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"`
}
type NegTokenTarg NegTokenResp
func (n *NegTokenInit ) Marshal () ([]byte , error ) {
m := marshalNegTokenInit {
MechTypes : n .MechTypes ,
ReqFlags : n .ReqFlags ,
MechTokenBytes : n .MechTokenBytes ,
MechListMIC : n .MechListMIC ,
}
b , err := asn1 .Marshal (m )
if err != nil {
return nil , err
}
nt := asn1 .RawValue {
Tag : 0 ,
Class : 2 ,
IsCompound : true ,
Bytes : b ,
}
nb , err := asn1 .Marshal (nt )
if err != nil {
return nil , err
}
return nb , nil
}
func (n *NegTokenInit ) Unmarshal (b []byte ) error {
init , nt , err := UnmarshalNegToken (b )
if err != nil {
return err
}
if !init {
return errors .New ("bytes were not that of a NegTokenInit" )
}
nInit := nt .(NegTokenInit )
n .MechTokenBytes = nInit .MechTokenBytes
n .MechListMIC = nInit .MechListMIC
n .MechTypes = nInit .MechTypes
n .ReqFlags = nInit .ReqFlags
return nil
}
func (n *NegTokenInit ) Verify () (bool , gssapi .Status ) {
var mtSupported bool
for _ , m := range n .MechTypes {
if m .Equal (gssapi .OIDKRB5 .OID ()) || m .Equal (gssapi .OIDMSLegacyKRB5 .OID ()) {
if n .mechToken == nil && n .MechTokenBytes == nil {
return false , gssapi .Status {Code : gssapi .StatusContinueNeeded }
}
mtSupported = true
break
}
}
if !mtSupported {
return false , gssapi .Status {Code : gssapi .StatusBadMech , Message : "no supported mechanism specified in negotiation" }
}
mt := new (KRB5Token )
mt .settings = n .settings
if n .mechToken == nil {
err := mt .Unmarshal (n .MechTokenBytes )
if err != nil {
return false , gssapi .Status {Code : gssapi .StatusDefectiveToken , Message : err .Error()}
}
n .mechToken = mt
} else {
var ok bool
mt , ok = n .mechToken .(*KRB5Token )
if !ok {
return false , gssapi .Status {Code : gssapi .StatusDefectiveToken , Message : "MechToken is not a KRB5 token as expected" }
}
}
return n .mechToken .Verify ()
}
func (n *NegTokenInit ) Context () context .Context {
if n .mechToken != nil {
mt , ok := n .mechToken .(*KRB5Token )
if !ok {
return nil
}
return mt .Context ()
}
return nil
}
func (n *NegTokenResp ) Marshal () ([]byte , error ) {
m := marshalNegTokenResp {
NegState : n .NegState ,
SupportedMech : n .SupportedMech ,
ResponseToken : n .ResponseToken ,
MechListMIC : n .MechListMIC ,
}
b , err := asn1 .Marshal (m )
if err != nil {
return nil , err
}
nt := asn1 .RawValue {
Tag : 1 ,
Class : 2 ,
IsCompound : true ,
Bytes : b ,
}
nb , err := asn1 .Marshal (nt )
if err != nil {
return nil , err
}
return nb , nil
}
func (n *NegTokenResp ) Unmarshal (b []byte ) error {
init , nt , err := UnmarshalNegToken (b )
if err != nil {
return err
}
if init {
return errors .New ("bytes were not that of a NegTokenResp" )
}
nResp := nt .(NegTokenResp )
n .MechListMIC = nResp .MechListMIC
n .NegState = nResp .NegState
n .ResponseToken = nResp .ResponseToken
n .SupportedMech = nResp .SupportedMech
return nil
}
func (n *NegTokenResp ) Verify () (bool , gssapi .Status ) {
if n .SupportedMech .Equal (gssapi .OIDKRB5 .OID ()) || n .SupportedMech .Equal (gssapi .OIDMSLegacyKRB5 .OID ()) {
if n .mechToken == nil && n .ResponseToken == nil {
return false , gssapi .Status {Code : gssapi .StatusContinueNeeded }
}
mt := new (KRB5Token )
mt .settings = n .settings
if n .mechToken == nil {
err := mt .Unmarshal (n .ResponseToken )
if err != nil {
return false , gssapi .Status {Code : gssapi .StatusDefectiveToken , Message : err .Error()}
}
n .mechToken = mt
} else {
var ok bool
mt , ok = n .mechToken .(*KRB5Token )
if !ok {
return false , gssapi .Status {Code : gssapi .StatusDefectiveToken , Message : "MechToken is not a KRB5 token as expected" }
}
}
if mt == nil {
return false , gssapi .Status {Code : gssapi .StatusContinueNeeded }
}
return mt .Verify ()
}
return false , gssapi .Status {Code : gssapi .StatusBadMech , Message : "no supported mechanism specified in negotiation" }
}
func (n *NegTokenResp ) State () NegState {
return NegState (n .NegState )
}
func (n *NegTokenResp ) Context () context .Context {
if n .mechToken != nil {
mt , ok := n .mechToken .(*KRB5Token )
if !ok {
return nil
}
return mt .Context ()
}
return nil
}
func UnmarshalNegToken (b []byte ) (bool , interface {}, error ) {
var a asn1 .RawValue
_ , err := asn1 .Unmarshal (b , &a )
if err != nil {
return false , nil , fmt .Errorf ("error unmarshalling NegotiationToken: %v" , err )
}
switch a .Tag {
case 0 :
var n marshalNegTokenInit
_, err = asn1 .Unmarshal (a .Bytes , &n )
if err != nil {
return false , nil , fmt .Errorf ("error unmarshalling NegotiationToken type %d (Init): %v" , a .Tag , err )
}
nt := NegTokenInit {
MechTypes : n .MechTypes ,
ReqFlags : n .ReqFlags ,
MechTokenBytes : n .MechTokenBytes ,
MechListMIC : n .MechListMIC ,
}
return true , nt , nil
case 1 :
var n marshalNegTokenResp
_, err = asn1 .Unmarshal (a .Bytes , &n )
if err != nil {
return false , nil , fmt .Errorf ("error unmarshalling NegotiationToken type %d (Resp/Targ): %v" , a .Tag , err )
}
nt := NegTokenResp {
NegState : n .NegState ,
SupportedMech : n .SupportedMech ,
ResponseToken : n .ResponseToken ,
MechListMIC : n .MechListMIC ,
}
return false , nt , nil
default :
return false , nil , errors .New ("unknown choice type for NegotiationToken" )
}
}
func NewNegTokenInitKRB5 (cl *client .Client , tkt messages .Ticket , sessionKey types .EncryptionKey ) (NegTokenInit , error ) {
mt , err := NewKRB5TokenAPREQ (cl , tkt , sessionKey , []int {gssapi .ContextFlagInteg , gssapi .ContextFlagConf }, []int {})
if err != nil {
return NegTokenInit {}, fmt .Errorf ("error getting KRB5 token; %v" , err )
}
mtb , err := mt .Marshal ()
if err != nil {
return NegTokenInit {}, fmt .Errorf ("error marshalling KRB5 token; %v" , err )
}
return NegTokenInit {
MechTypes : []asn1 .ObjectIdentifier {gssapi .OIDKRB5 .OID ()},
MechTokenBytes : mtb ,
}, nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .